#pragma once
#include <cstddef>
#include <initializer_list>
#include <utility>

template<class T>
struct list_node
{
    T data_;
    list_node<T> *next_;
    list_node<T> *prev_;

    list_node(const T &value = T())
        : data_(value)
        , next_(nullptr)
        , prev_(nullptr)
    {}
};

template<class T, class Ref, class Ptr>
struct list_iterator__
{
    using Node = list_node<T>;
    using Self = list_iterator__<T, Ref, Ptr>;
    Node *node_;
    list_iterator__(Node *node)
        : node_(node)
    {}
    Ref operator*()
    {
        return node_->data_;
    }
    Ptr operator->()
    {
        return &node_->data_;
    }
    Self &operator++()
    {
        node_ = node_->next_;
        return *this;
    }
    Self operator++(int)
    {
        Self tmp(*this);
        node_ = node_->next_;
        return tmp;
    }
    Self &operator--()
    {
        node_ = node_->prev_;
        return *this;
    }
    Self operator--(int)
    {
        Self tmp(*this);
        node_ = node_->prev_;
        return tmp;
    }
    bool operator!=(const Self &it)
    {
        return it.node_ != node_;
    }
    bool operator==(const Self &it)
    {
        return it.node_ == node_;
    }
};

template<class T>
class list
{
    using Node = list_node<T>;

public:
    using iterator = list_iterator__<T, T &, T *>;
    using const_iterator = list_iterator__<T, const T &, const T *>;
    
    iterator begin()
    {
        return iterator(head_->next_);
    }
    iterator end()
    {
        return iterator(head_);
    }
    void empty_init()
    {
        head_ = new Node;
        head_->next_ = head_;
        head_->prev_ = head_;
    }
    list()
    {
        empty_init();
    }
    list(list &lt)
    {
        empty_init();
        for (auto &e : lt)
            push_back(e);
    }
    list(std::initializer_list<T> il)
    {
        empty_init();
        for (auto &e : il)
            push_back(e);
    }
    void swap(list<T> &lt)
    {
        std::swap(lt.head_, head_);
        std::swap(lt.size_, size_);
    }
    list &operator=(list lt)
    {
        swap(lt);
        return *this;
    }
    ~list()
    {
        clear();
        delete head_;
        head_ = nullptr;
    }
    void clear()
    {
        iterator it = begin();
        while (it != end())
            it = erase(it);
    }
    void push_back(const T &value)
    {
        insert(end(), value);
    }
    void push_front(const T &value)
    {
        insert(begin(), value);
    }
    iterator insert(iterator pos, const T &value)
    {
        Node *newNode = new Node(value);
        Node *current = pos.node_;
        Node *prev = current->prev_;

        ++size_;
        // prev newNode current
        prev->next_ = newNode;
        newNode->next_ = current;
        current->prev_ = newNode;
        newNode->prev_ = prev;
        return iterator(newNode);
    }
    void pop_back()
    {
        erase(--end());
    }
    void pop_front()
    {
        erase(begin());
    }
    iterator erase(iterator pos)
    {
        Node *current = pos.node_;
        Node *prev = current->prev_;
        Node *next = current->next_;

        --size_;
        prev->next_ = next;
        next->prev_ = prev;
        delete current;
        return iterator(next);
    }
    size_t size() const
    {
        return size_;
    }

private:
    Node *head_;
    size_t size_ = 0;
};